/*
 * Copyright (c) 2022 ASR Microelectronics (Shanghai) Co., Ltd. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "asr_i2s.h"
#include "asr_rf_spi.h"
#include "asr_port_peripheral.h"

asr_i2s_callback_func g_asr_i2s_callback_handler;

void asr_i2s_enable_irq(void)
{
    port_i2s_enable_irq();
}

void asr_i2s_disable_irq(void)
{
    port_i2s_disable_irq();
}

void asr_i2s_struct_init(asr_i2s_dev_t * pI2S_struct)
{
    pI2S_struct->i2s_role = I2S_ROLE_MASTER;
    pI2S_struct->i2s_word_size = I2S_WORDSIZE_32bit;
    pI2S_struct->i2s_tx_en = ENABLE;
    pI2S_struct->i2s_rx_en = ENABLE;
    pI2S_struct->i2s_fifo_threshold = I2S_FIFO_TRIGGERL_LEVEL_4;
    pI2S_struct->i2s_sample_rate = I2S_SAMPLE_RATE_44P1K;
    pI2S_struct->i2s_mclk_src = I2S_MCLK_SRC_FREQ120;
    pI2S_struct->i2s_ws  = 32;
    pI2S_struct->i2s_mode = I2S_MODE_PHILIPS;
}

void asr_i2s_interrupt_config(I2S_TypeDef * I2Sx, uint32_t i2s_interrupt, uint32_t new_state)
{
    if( new_state == ENABLE)
    {
        I2Sx->IMR &= ~i2s_interrupt;
    }
    else
    {
        I2Sx->IMR |= i2s_interrupt;
    }
}

void asr_i2s_interrupt_clear(I2S_TypeDef * I2Sx, uint32_t i2s_interrupt)
{
    if( i2s_interrupt == I2S_INTERRUPT_TXFO )
        I2Sx->TOR; // read to clear interrupt
    else if( i2s_interrupt == I2S_INTERRUPT_RXFO )
        I2Sx->ROR;
}

void asr_i2s_cmd(I2S_TypeDef * I2Sx, uint32_t new_state)
{
    if( new_state == ENABLE )
    {
        I2Sx->IER |= ENABLE;
    }
    else
    {
        I2Sx->IER &= DISABLE;
    }
}

void asr_i2s_tx_block_cmd(I2S_TypeDef * I2Sx, uint32_t new_state)
{
    if( new_state == ENABLE )
    {
        I2Sx->ITER |= ENABLE;
    }
    else
    {
        I2Sx->ITER &= DISABLE;
    }
}

void asr_i2s_rx_block_cmd(I2S_TypeDef * I2Sx, uint32_t new_state)
{
    if( new_state == ENABLE )
    {
        I2Sx->IRER |= ENABLE;
    }
    else
    {
        I2Sx->IRER &= DISABLE;
    }
}

void asr_i2s_tx_channel_cmd(I2S_TypeDef * I2Sx, uint32_t new_state)
{
    if( new_state == ENABLE )
    {
        I2Sx->TER |= ENABLE;
    }
    else
    {
        I2Sx->TER &= DISABLE;
    }
}

void asr_i2s_rx_channel_cmd(I2S_TypeDef * I2Sx, uint32_t new_state)
{
    if( new_state == ENABLE )
    {
        I2Sx->RER |= ENABLE;
    }
    else
    {
        I2Sx->RER &= DISABLE;
    }
}

void asr_i2s_master_clock_cmd(I2S_TypeDef * I2Sx, uint32_t new_state)
{
    if( new_state == ENABLE)
    {
        I2Sx->CER |= ENABLE;
        port_i2s_enable_clk(); //open clock source of i2s
    }
    else
    {
        I2Sx->CER &= DISABLE;
        port_i2s_disable_clk(); //close clock source of i2s
    }
}

int asr_i2s_config_apll_clk(uint32_t clk_src)
{
    return port_i2s_config_clk(clk_src);
}

int asr_i2s_init(I2S_TypeDef * I2Sx, asr_i2s_dev_t * pI2S_struct)
{
    uint32_t mclk_divider = 0;
    uint32_t sclk_divider = 0;
    uint32_t lrclk_divider = 0;

    if(pI2S_struct->i2s_role == I2S_ROLE_MASTER)
    {
        if(pI2S_struct->i2s_sample_rate == I2S_SAMPLE_RATE_44P1K)
        {
            if(pI2S_struct->i2s_mclk_src == I2S_MCLK_SRC_FREQ120
                ||  pI2S_struct->i2s_mclk_src == I2S_MCLK_SRC_FREQ96
                ||  pI2S_struct->i2s_mclk_src == I2S_MCLK_SRC_FREQ72)
            {
                mclk_divider = 17;
            }
        }
        else if(pI2S_struct->i2s_sample_rate == I2S_SAMPLE_RATE_96K
                || pI2S_struct->i2s_sample_rate == I2S_SAMPLE_RATE_48K
                || pI2S_struct->i2s_sample_rate == I2S_SAMPLE_RATE_32K
                || pI2S_struct->i2s_sample_rate == I2S_SAMPLE_RATE_16K
                || pI2S_struct->i2s_sample_rate == I2S_SAMPLE_RATE_8K)
        {
            if(pI2S_struct->i2s_mclk_src == I2S_MCLK_SRC_FREQ120)
            {
                mclk_divider = 13;
            }
        }

        if(mclk_divider == 0)
            return EIO;

        lrclk_divider = 2*pI2S_struct->i2s_ws;
        sclk_divider = pI2S_struct->i2s_mclk_src / mclk_divider / pI2S_struct->i2s_sample_rate / lrclk_divider;
    }

    if(asr_i2s_config_apll_clk(pI2S_struct->i2s_mclk_src) != 0)
        return EIO;

    asr_i2s_master_clock_cmd(I2Sx, ENABLE);
    asr_i2s_cmd(I2Sx, DISABLE);

    I2Sx->RCR &= ~I2S_RX_WORDSIZE_MASK;
    I2Sx->RCR |= pI2S_struct->i2s_word_size;

    I2Sx->TCR &= ~I2S_TX_WORDSIZE_MASK;
    I2Sx->TCR |= pI2S_struct->i2s_word_size;

    I2Sx->RFCR &= ~I2S_FIFO_TRIGGER_LEVEL_MASK;
    I2Sx->RFCR |= pI2S_struct->i2s_fifo_threshold;

    I2Sx->TFCR &= ~I2S_FIFO_TRIGGER_LEVEL_MASK;
    I2Sx->TFCR |= pI2S_struct->i2s_fifo_threshold;
    I2Sx->RFF |= 0x1;
    I2Sx->TFF |= 0x1;
    if(pI2S_struct->i2s_tx_en == ENABLE)
    {
        asr_i2s_tx_block_cmd(I2Sx, ENABLE);
        asr_i2s_tx_channel_cmd(I2Sx, ENABLE);
    }
    else
    {
        asr_i2s_tx_block_cmd(I2Sx, DISABLE);
        asr_i2s_tx_channel_cmd(I2Sx, DISABLE);
    }

    if(pI2S_struct->i2s_rx_en == ENABLE)
    {

        asr_i2s_rx_block_cmd(I2Sx, ENABLE);
        asr_i2s_rx_channel_cmd(I2Sx, ENABLE);
    }
    else
    {
        asr_i2s_rx_block_cmd(I2Sx, DISABLE);
        asr_i2s_rx_channel_cmd(I2Sx, DISABLE);
    }

    if(pI2S_struct->i2s_role == I2S_ROLE_MASTER)
    {
        I2S_CLK_DIV->i2s_mclk_divider = mclk_divider - 1;
        I2S_CLK_DIV->i2s_sclk_divider = sclk_divider - 1;
        I2S_CLK_DIV->i2s_lrclk_divider = lrclk_divider - 1;
        I2S_CLK_DIV->i2s_slave_mode = 0; //set to I2S master
    }
    else
    {
        I2S_CLK_DIV->i2s_slave_mode = 1; //set to I2S slave
    }

    asr_i2s_enable_irq();
    return 0;
}

uint32_t asr_i2s_receive_data(I2S_TypeDef * I2Sx, uint8_t lr)
{
    if(lr == 0) //left channel
        return I2Sx->LRBR_LTHR;
    else   // right channel
        return I2Sx->RRBR_RTHR;
}

void asr_i2s_send_data(I2S_TypeDef * I2Sx, uint32_t* left_chan_data, uint32_t* right_chan_data, uint32_t len)
{
    while(len)
    {
        while( !i2s_get_interrupt_status(I2Sx, I2S_INTERRUPT_TXFE) ); // wait till tx fifo emptys
        for( int i = 0; i < I2S_FIFO_DEPTH && len > 0; i++, len--)
        {
            I2Sx->LRBR_LTHR = *left_chan_data++;
            I2Sx->RRBR_RTHR = *right_chan_data++;
        }
    }
    while( !i2s_get_interrupt_status(I2Sx, I2S_INTERRUPT_TXFE) ); // wait till tx fifo emptys
}

void I2S_IRQHandler()
{
    uint32_t g_data_l = 0;
    uint32_t g_data_r = 0;
    if( i2s_get_interrupt_status(I2S,I2S_INTERRUPT_RXDA) )  // rx data available
    {
        for(int i = 0; i < (I2S->RFCR & I2S_FIFO_TRIGGER_LEVEL_MASK); i++)
        {
            g_data_l = I2S->LRBR_LTHR;
            g_data_r = I2S->RRBR_RTHR;
            if(g_asr_i2s_callback_handler != NULL)
            {
                g_asr_i2s_callback_handler(g_data_l,g_data_r);
            }
        }
    }
}
